3fbba6dc38q-ioRlwSR_quw4G3qUeQ tools/xc/lib/xc_vif.c
3fbd0a3dTwnDcfdw0-v46dPbX98zDw tools/xc/py/Makefile
3fbd0a42l40lM0IICw2jXbQBVZSdZg tools/xc/py/Xc.c
-3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py
3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
+4055ee41IfFazrwadCH2J72nz-A9YA tools/xenctl/Makefile
+4055ee4b_4Rvns_KzE12csI14EKK6Q tools/xenctl/lib/__init__.py
+4055ee4dwy4l0MghZosxoiu6zmhc9Q tools/xenctl/lib/console_client.py
+3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xenctl/lib/utils.py
+4055ee44Bu6oP7U0WxxXypbUt4dNPQ tools/xenctl/setup.py
40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend/Makefile
4055ad95Se-FqttgxollqOAAHB94zA tools/xend/lib/__init__.py
4055ad97wMLUj0BZT0e_T0EwQN0Bvw tools/xend/lib/console.py
them to the standard directories with 'make install', or into the
../install subtree with 'make dist'.
-/usr/local/bin/xc_* the domain control tools
-/lib/libxc.so the xc library
-/usr/lib/python2.2/site-packages/XenoUtil.py python util library
-/usr/lib/python2.2/site-packages/Xc.c python xc bindings
-
If you're using the virtual disk control tools (xc_vd_tool) you'll
need the SQLite library and python binding pysqlite. There's a tar
ball containing the necessary binaries on the project downloads page.
new virtual disk extents.
The functionality provided by these scripts is also available directly from
-Python functions in the XenoUtil module - you can use this functionality in
+Python functions in the xenctl.utils module - you can use this functionality in
your own scripts.
Populating VDs:
In this way you can see that the class 'xc' contains useful
documentation for you to consult.
-A further module of useful routines (XenoUtil) is also installed:
+A further package of useful routines (xenctl) is also installed:
-# import XenoUtil
-# help(XenoUtil)
+# import xenctl.utils
+# help(xenctl.utils)
You can use these modules to write your own custom scripts or you can
customise the scripts supplied in the Xen distribution.
$(MAKE) -C misc
$(MAKE) -C examples
$(MAKE) -C xentrace
+ $(MAKE) -C xenctl
$(MAKE) -C xend
install: all
$(MAKE) -C misc install
$(MAKE) -C examples install
$(MAKE) -C xentrace install
+ $(MAKE) -C xenctl install
$(MAKE) -C xend install
clean:
$(MAKE) -C examples clean
$(MAKE) -C nsplitd clean
$(MAKE) -C xentrace clean
+ $(MAKE) -C xenctl clean
$(MAKE) -C xend clean
For many operations you will either be able to use these scripts directly, or
incorporate code from them into your own scripts.
-The Xc and XenoUtil Python modules provide an API for accessing all this
+The Xc and xenctl.utils Python modules provide an API for accessing all this
functionality - and more - from your own Python programs. These libraries may
contain features for which there aren't yet example scripts written for...
# appropriately.
#vfr_ipaddr = ["111.222.333.444","222.333.444.555"]
-vfr_ipaddr = [XenoUtil.add_offset_to_ip(XenoUtil.get_current_ipaddr(),vmid),
- XenoUtil.add_offset_to_ip('169.254.1.0',vmid),]
+vfr_ipaddr = [xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid),
+ xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),]
# STEP 5a. Identify any physcial partitions or virtual disks you want the
# You can use 'extrabit' to set the runlevel and custom environment
# variables used by custom rc scripts (e.g. VMID=, usr= )
-netmask = XenoUtil.get_current_ipmask()
-gateway = XenoUtil.get_current_ipgw()
+netmask = xenctl.utils.get_current_ipmask()
+gateway = xenctl.utils.get_current_ipgw()
nfsserv = '169.254.1.0'
cmdline_ip = "ip="+vfr_ipaddr[0]+":"+nfsserv+":"+gateway+":"+netmask+"::eth0:off"
# appropriately.
#vfr_ipaddr = ["111.222.333.444","222.333.444.555"]
-#vfr_ipaddr = [XenoUtil.add_offset_to_ip(XenoUtil.get_current_ipaddr(),vmid)]
+#vfr_ipaddr = [xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid)]
vfr_ipaddr = map(socket.gethostbyname,string.split(ip,','))
# see if we have a local IP at all
localip=''
for i in vfr_ipaddr:
- if XenoUtil.check_subnet(i,'169.254.0.0','255.255.0.0'):
+ if xenctl.utils.check_subnet(i,'169.254.0.0','255.255.0.0'):
localip=i
break
myip = vfr_ipaddr[0]
except NameError:
- netmask = XenoUtil.get_current_ipmask()
- gateway = XenoUtil.get_current_ipgw()
+ netmask = xenctl.utils.get_current_ipmask()
+ gateway = xenctl.utils.get_current_ipgw()
# if we haven't got an address, see if we have one that matches the LAN
if not myip:
if netmask and gateway:
for i in vfr_ipaddr:
- if XenoUtil.check_subnet(i,gateway,netmask):
+ if xenctl.utils.check_subnet(i,gateway,netmask):
myip=i
break
#vfr_ipaddr = ["111.222.333.444","222.333.444.555"]
try:
- vfr_ipaddr = [ip, XenoUtil.add_offset_to_ip('169.254.1.0',vmid),]
+ vfr_ipaddr = [ip, xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),]
except:
- vfr_ipaddr = [XenoUtil.add_offset_to_ip(XenoUtil.get_current_ipaddr(),vmid),
- XenoUtil.add_offset_to_ip('169.254.1.0',vmid),]
+ vfr_ipaddr = [xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid),
+ xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),]
# STEP 5a. Identify any physcial partitions or virtual disks you want the
# You can use 'extrabit' to set the runlevel and custom environment
# variables used by custom rc scripts (e.g. VMID=, usr= )
-netmask = XenoUtil.get_current_ipmask()
-gateway = XenoUtil.get_current_ipgw()
+netmask = xenctl.utils.get_current_ipmask()
+gateway = xenctl.utils.get_current_ipgw()
try:
nfsserv
except:
ip = sys.argv[4]
# XXX This function should be moved to Xc once we sort out the VFR
- import XenoUtil
- XenoUtil.setup_vfr_rules_for_vif( dom, vif, ip )
+ import xenctl.utils
+ xenctl.utils.setup_vfr_rules_for_vif( dom, vif, ip )
elif cmd == 'vif_setsched':
if len(sys.argv) < 6:
elif cmd == 'vbd_add':
- import XenoUtil
+ import xenctl.utils
- XenoUtil.VBD_EXPERT_LEVEL = 0 # sets the allowed level of potentially unsafe mappings
+ xenctl.utils.VBD_EXPERT_LEVEL = 0 # sets the allowed level of potentially unsafe mappings
if len(sys.argv) < 6:
usage()
if mode == 'rw' or mode == 'w':
writeable = 1;
- segments = XenoUtil.lookup_disk_uname(uname)
+ segments = xenctl.utils.lookup_disk_uname(uname)
if not segments:
print "Lookup Failed"
sys.exit(1)
- if XenoUtil.vd_extents_validate(segments,writeable) < 0:
+ if xenctl.utils.vd_extents_validate(segments,writeable) < 0:
print "That mapping is too unsafe for the current VBD expertise level"
sys.exit(1)
- virt_dev = XenoUtil.blkdev_name_to_number(dev)
+ virt_dev = xenctl.utils.blkdev_name_to_number(dev)
xc.vbd_create(dom,virt_dev,writeable)
print "Added disk/partition %s to domain %d as device %s (%x)" % (uname, dom, dev, virt_dev)
elif cmd == 'vbd_remove':
- import XenoUtil
+ import xenctl.utils
if len(sys.argv) < 4:
usage()
sys.exit(1)
dev = sys.argv[3]
- virt_dev = XenoUtil.blkdev_name_to_number(dev)
+ virt_dev = xenctl.utils.blkdev_name_to_number(dev)
if not xc.vbd_destroy(dom,virt_dev):
print "Removed disk/partition attached as device %s (%x) in domain %d" % (dev, virt_dev, dom)
#!/usr/bin/env python
-import Xc, XenoUtil, string, sys, os, time, socket, getopt, signal, syslog
+import string, sys, os, time, socket, getopt, signal, syslog
+import Xc, xenctl.utils, xenctl.console_client
config_dir = '/etc/xc/'
config_file = xc_config_file = config_dir + 'defaults'
def extra_usage ():
print >>sys.stderr,"""
Arguments to override current config read from '%s':
+ -c -- Turn into console terminal after domain is created
-k image -- Path to kernel image ['%s']
-r ramdisk -- Path to ramdisk (or empty) ['%s']
-b builder_fn -- Function to use to build domain ['%s']
vbd_expert=0; auto_restart=False;
vbd_list = []; cmdline_ip = ''; cmdline_root=''; cmdline_extra=''
pci_device_list = []
+auto_console = False
##### Determine location of defautls file
#####
try:
- opts, args = getopt.getopt(sys.argv[1:], "h?nqf:D:k:r:b:m:N:a:e:d:i:I:R:E:L:" )
+ opts, args = getopt.getopt(sys.argv[1:], "h?nqcf:D:k:r:b:m:N:a:e:d:i:I:R:E:L:" )
for opt in opts:
if opt[0] == '-f': config_file= opt[1]
if opt[0] == '-R': cmdline_root = opt[1]
if opt[0] == '-E': cmdline_extra = opt[1]
if opt[0] == '-i': x_vfr_ipaddr.append(opt[1])
+ if opt[0] == '-c': auto_console = True
if opt[0] == '-d':
try:
vv = string.split(opt[1],';')
sys.exit()
cmsg = 'new_control_interface(dom='+str(id)+')'
- xend_response = XenoUtil.xend_control_message(cmsg)
+ xend_response = xenctl.utils.xend_control_message(cmsg)
if not xend_response['success']:
print "Error creating initial event channel"
print "Error type: " + xend_response['error_type']
# setup the virtual block devices
# set the expertise level appropriately
- XenoUtil.VBD_EXPERT_MODE = vbd_expert
+ xenctl.utils.VBD_EXPERT_MODE = vbd_expert
for ( uname, virt_name, rw ) in vbd_list:
- virt_dev = XenoUtil.blkdev_name_to_number( virt_name )
+ virt_dev = xenctl.utils.blkdev_name_to_number( virt_name )
- segments = XenoUtil.lookup_disk_uname( uname )
+ segments = xenctl.utils.lookup_disk_uname( uname )
if not segments:
print "Error looking up %s\n" % uname
xc.domain_destroy ( dom=id )
# check that setting up this VBD won't violate the sharing
# allowed by the current VBD expertise level
- if XenoUtil.vd_extents_validate(segments, rw=='w' or rw=='rw') < 0:
+ if xenctl.utils.vd_extents_validate(segments, rw=='w' or rw=='rw') < 0:
xc.domain_destroy( dom = id )
sys.exit()
# setup virtual firewall rules for all aliases
for ip in vfr_ipaddr:
- XenoUtil.setup_vfr_rules_for_vif( id, 0, ip )
+ xenctl.utils.setup_vfr_rules_for_vif( id, 0, ip )
# check for physical device access
for (pci_bus, pci_dev, pci_func) in pci_device_list:
(current_id, current_port) = make_domain()
output("VM started in domain %d. Console I/O available on TCP port %d." % (current_id,current_port))
+if auto_console:
+ xenctl.console_client.connect('127.0.0.1',int(current_port))
+
# if the auto_restart flag is set then keep polling to see if the domain is
# alive - restart if it is not by calling make_domain() again (it's necessary
# to update the id variable, since the new domain may have a new ID)
#!/usr/bin/env python
-import XenoUtil, sys, re, string
+import xenctl.utils, sys, re, string
def usage():
print "Device: " + dev
print "Extent size: " + str(extent_size) + "MB"
- rc = XenoUtil.vd_format(dev, extent_size)
+ rc = xenctl.utils.vd_format(dev, extent_size)
elif cmd == 'create':
print "Size: %d" % size
print "Expiry time (seconds from now): %d" % expiry_time
- src = XenoUtil.vd_create(size, expiry_time)
+ src = xenctl.utils.vd_create(size, expiry_time)
elif cmd == 'enlarge':
extra_size = int(sys.argv[3])
- rc = XenoUtil.vd_enlarge(id, extra_size)
+ rc = xenctl.utils.vd_enlarge(id, extra_size)
elif cmd == 'delete':
print "Deleting a virtual disk with ID: " + id
- rc = XenoUtil.vd_delete(id)
+ rc = xenctl.utils.vd_delete(id)
elif cmd == 'import':
print "Allocate new virtual disk and populate from file : %s" % file
- print XenoUtil.vd_read_from_file(file, expiry_time)
+ print xenctl.utils.vd_read_from_file(file, expiry_time)
elif cmd == 'export':
print "Dump contents of virtual disk to file : %s" % file
- rc = XenoUtil.vd_cp_to_file(id, file )
+ rc = xenctl.utils.vd_cp_to_file(id, file )
elif cmd == 'setexpiry':
print "Id: " + id
print "Expiry time (seconds from now [or 0]): " + str(expiry_time)
- rc = XenoUtil.vd_refresh(id, expiry_time)
+ rc = xenctl.utils.vd_refresh(id, expiry_time)
elif cmd == 'list':
print 'ID Size(MB) Expiry'
- for vbd in XenoUtil.vd_list():
+ for vbd in xenctl.utils.vd_list():
vbd['size_mb'] = vbd['size'] / 2048
vbd['expiry'] = (vbd['expires'] and vbd['expiry_time']) or 'never'
print '%(vdisk_id)-4s %(size_mb)-12d %(expiry)s' % vbd
elif cmd == 'freespace':
- print XenoUtil.vd_freespace()
+ print xenctl.utils.vd_freespace()
elif cmd == 'undelete':
if len(sys.argv) > 3:
expiry_time = int(sys.argv[3])
- if XenoUtil.vd_undelete(id, expiry_time):
+ if xenctl.utils.vd_undelete(id, expiry_time):
print "Undelete operation failed for virtual disk: " + id
else:
print "Undelete operation succeeded for virtual disk: " + id
+++ /dev/null
-import os, re, socket, string, sys, tempfile
-
-##### Module variables
-
-"""Location of the Virtual Disk management database.
- defaults to /var/db/xen_vdisks.sqlite
-"""
-VD_DB_FILE = "/var/db/xen_vdisks.sqlite"
-
-"""VBD expertise level - determines the strictness of the sanity checking.
- This mode determines the level of complaints when disk sharing occurs
- through the current VBD mappings.
- 0 - only allow shared mappings if both domains have r/o access (always OK)
- 1 - also allow sharing with one dom r/w and the other r/o
- 2 - allow sharing with both doms r/w
-"""
-VBD_EXPERT_MODE = 0
-
-##### Module initialisation
-
-try:
- # try to import sqlite (not everyone will have it installed)
- import sqlite
-except ImportError:
- # on failure, just catch the error, don't do anything
- pass
-
-
-##### Networking-related functions
-
-def get_current_ipaddr(dev='eth0'):
- """Return a string containing the primary IP address for the given
- network interface (default 'eth0').
- """
- fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
- lines = fd.readlines()
- for line in lines:
- m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
- line )
- if m:
- return m.group(1)
- return None
-
-def get_current_ipmask(dev='eth0'):
- """Return a string containing the primary IP netmask for the given
- network interface (default 'eth0').
- """
- fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
- lines = fd.readlines()
- for line in lines:
- m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
- line )
- if m:
- return m.group(1)
- return None
-
-def get_current_ipgw(dev='eth0'):
- """Return a string containing the IP gateway for the given
- network interface (default 'eth0').
- """
- fd = os.popen( '/sbin/route -n' )
- lines = fd.readlines()
- for line in lines:
- m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
- '\s+\S+\s+\S*G.*' + dev + '.*', line )
- if m:
- return m.group(1)
- return None
-
-def setup_vfr_rules_for_vif(dom,vif,addr):
- """Takes a tuple ( domain-id, vif-id, ip-addr ), where the ip-addr
- is expressed as a textual dotted quad, and set up appropriate routing
- rules in Xen. No return value.
- """
- fd = os.open( '/proc/xen/vfr', os.O_WRONLY )
- if ( re.search( '169\.254', addr) ):
- os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
- ' srcaddrmask=255.255.255.255' +
- ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
- ' dstdom=0 dstidx=0 proto=any\n' )
- else:
- os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
- ' srcaddrmask=255.255.255.255' +
- ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
- ' dst=PHYS proto=any\n' )
- os.write( fd, 'ADD ACCEPT dstaddr=' + addr +
- ' dstaddrmask=255.255.255.255' +
- ' src=ANY' +
- ' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
- ' proto=any\n' )
- os.close( fd )
- return None
-
-def add_offset_to_ip( ip, off ):
- l = string.split(ip,'.')
- a = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
- (string.atoi(l[2])<<8) | string.atoi(l[3]) ) + off
-
- return '%d.%d.%d.%d' % ( ((a>>24)&0xff), ((a>>16)&0xff),
- ((a>>8)&0xff), (a&0xff) )
-
-def check_subnet( ip, network, netmask ):
- l = string.split(ip,'.')
- n_ip = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
- (string.atoi(l[2])<<8) | string.atoi(l[3]) )
-
- l = string.split(network,'.')
- n_net = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
- (string.atoi(l[2])<<8) | string.atoi(l[3]) )
-
- l = string.split(netmask,'.')
- n_mask = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
- (string.atoi(l[2])<<8) | string.atoi(l[3]) )
-
- return (n_ip&n_mask)==(n_net&n_mask)
-
-
-##### VBD-related Functions
-
-def blkdev_name_to_number(name):
- """Take the given textual block-device name (e.g., '/dev/sda1',
- 'hda') and return the device number used by the OS. """
-
- if not re.match( '/dev/', name ):
- name = '/dev/' + name
-
- return os.stat(name).st_rdev
-
-# lookup_blkdev_partn_info( '/dev/sda3' )
-def lookup_raw_partn(partition):
- """Take the given block-device name (e.g., '/dev/sda1', 'hda')
- and return a dictionary { device, start_sector,
- nr_sectors, type }
- device: Device number of the given partition
- start_sector: Index of first sector of the partition
- nr_sectors: Number of sectors comprising this partition
- type: 'Disk' or identifying name for partition type
- """
-
- if not re.match( '/dev/', partition ):
- partition = '/dev/' + partition
-
- drive = re.split( '[0-9]', partition )[0]
-
- if drive == partition:
- fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
- line = fd.readline()
- if line:
- return [ { 'device' : blkdev_name_to_number(drive),
- 'start_sector' : long(0),
- 'nr_sectors' : long(line) * 2,
- 'type' : 'Disk' } ]
- return None
-
- # determine position on disk
- fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
-
- #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
- lines = fd.readlines()
- for line in lines:
- m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
- 'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
- if m:
- return [ { 'device' : blkdev_name_to_number(drive),
- 'start_sector' : long(m.group(1)),
- 'nr_sectors' : long(m.group(2)),
- 'type' : m.group(3) } ]
-
- return None
-
-def lookup_disk_uname( uname ):
- """Lookup a list of segments for either a physical or a virtual device.
- uname [string]: name of the device in the format \'vd:id\' for a virtual
- disk, or \'phy:dev\' for a physical device
- returns [list of dicts]: list of extents that make up the named device
- """
- ( type, d_name ) = string.split( uname, ':' )
-
- if type == "phy":
- segments = lookup_raw_partn( d_name )
- elif type == "vd":
- segments = vd_lookup( d_name )
-
- return segments
-
-
-##### Management of the Xen control daemon
-##### (c) Keir Fraser, University of Cambridge
-
-def xend_control_message( message ):
- """Takes a textual control message and sends it to the 'xend' Xen
- control daemon. Returns a dictionary containing the daemon's multi-part
- response."""
- tmpdir = tempfile.mkdtemp()
- try:
- ctl = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
- ctl.bind(tmpdir+'/sock')
- ctl.sendto(message, '/var/run/xend/management_sock')
- data, addr = ctl.recvfrom(2048)
- ctl.close()
- finally:
- if os.path.exists(tmpdir+'/sock'):
- os.unlink(tmpdir+'/sock')
- if os.path.exists(tmpdir):
- os.rmdir(tmpdir)
- return eval(data)
-
-
-##### VD Management-related functions
-
-##### By Mark Williamson, <mark.a.williamson@intel.com>
-##### (C) Intel Research Cambridge
-
-# TODO:
-#
-# Plenty of room for enhancement to this functionality (contributions
-# welcome - and then you get to have your name in the source ;-)...
-#
-# vd_unformat() : want facilities to unallocate virtual disk
-# partitions, possibly migrating virtual disks of them, with checks to see if
-# it's safe and options to force it anyway
-#
-# vd_create() : should have an optional argument specifying a physical
-# disk preference - useful to allocate for guest doms to do RAID
-#
-# vd_undelete() : add ability to "best effort" undelete as much of a
-# vdisk as is left in the case that some of it has already been
-# reallocated. Some people might still be able to recover some of
-# their data this way, even if some of the disk has disappeared.
-#
-# It'd be nice if we could wipe virtual disks for security purposes -
-# should be easy to do this using dev if=/dev/{zero,random} on each
-# extent in turn. There could be another optional flag to vd_create
-# in order to allow this.
-#
-# Error codes could be more expressive - i.e. actually tell why the
-# error occurred rather than "it broke". Currently the code avoids
-# using exceptions to make control scripting simpler and more
-# accessible to beginners - therefore probably should just use more
-# return codes.
-#
-# Enhancements / additions to the example scripts are also welcome:
-# some people will interact with this code mostly through those
-# scripts.
-#
-# More documentation of how this stuff should be used is always nice -
-# if you have a novel configuration that you feel isn't discussed
-# enough in the HOWTO (which is currently a work in progress), feel
-# free to contribute a walkthrough, or something more substantial.
-#
-
-
-def __vd_no_database():
- """Called when no database found - exits with an error
- """
- print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE
- sys.exit(1)
-
-
-def vd_format(partition, extent_size_mb):
- """Format a partition or drive for use a virtual disk storage.
- partition [string]: device file representing the partition
- extent_size_mb [string]: extent size in megabytes to use on this disk
- """
-
- if not os.path.isfile(VD_DB_FILE):
- vd_init_db(VD_DB_FILE)
-
- if not re.match( '/dev/', partition ):
- partition = '/dev/' + partition
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("select * from vdisk_part where partition = \'"
- + partition + "\'")
- row = cu.fetchone()
-
- extent_size = extent_size_mb * 2048 # convert megabytes to sectors
-
- if not row:
- part_info = lookup_raw_partn(partition)[0]
-
- cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " +
- "VALUES ( \'" + partition + "\', "
- + str(blkdev_name_to_number(partition))
- + ", " + str(extent_size) + ")")
-
-
- cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents "
- + "WHERE vdisk_id = 0")
-
- max_id, = cu.fetchone()
-
- if max_id != None:
- new_id = max_id + 1
- else:
- new_id = 0
-
- num_extents = part_info['nr_sectors'] / extent_size
-
- for i in range(num_extents):
- sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id,
- part_id, part_extent_no)
- VALUES ("""+ str(new_id + i) + ", 0, "\
- + str(blkdev_name_to_number(partition))\
- + ", " + str(num_extents - (i + 1)) + ")"
- cu.execute(sql)
-
- cx.commit()
- cx.close()
- return 0
-
-
-def vd_create(size_mb, expiry):
- """Create a new virtual disk.
- size_mb [int]: size in megabytes for the new virtual disk
- expiry [int]: expiry time in seconds from now
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- size = size_mb * 2048
-
- cu.execute("SELECT max(vdisk_id) FROM vdisks")
- max_id, = cu.fetchone()
- new_id = int(max_id) + 1
-
- # fetch a list of extents from the expired disks, along with information
- # about their size
- cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
- vdisk_extents.part_id, extent_size
- FROM vdisks NATURAL JOIN vdisk_extents
- NATURAL JOIN vdisk_part
- WHERE expires AND expiry_time <= datetime('now')
- ORDER BY expiry_time ASC, vdisk_extent_no DESC
- """) # aims to reuse the last extents
- # from the longest-expired disks first
-
- allocated = 0
-
- if expiry:
- expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
- expires = 1
- else:
- expiry_ts = "NULL"
- expires = 0
-
- # we'll use this to build the SQL statement we want
- building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
- +" VALUES ("+str(new_id)+", "+str(size)+ ", " \
- + str(expires) + ", " + expiry_ts + "); "
-
- counter = 0
-
- while allocated < size:
- row = cu.fetchone()
- if not row:
- print "ran out of space, having allocated %d meg of %d" % (allocated, size)
- cx.close()
- return -1
-
-
- (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
- allocated += extent_size
- building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \
- + ", " + "vdisk_extent_no = " + str(counter) \
- + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
- + " AND vdisk_id = " + str(vdisk_id) + "; "
-
- counter += 1
-
-
- # this will execute the SQL query we build to store details of the new
- # virtual disk and allocate space to it print building_sql
- cu.execute(building_sql)
-
- cx.commit()
- cx.close()
- return str(new_id)
-
-
-def vd_lookup(id):
- """Lookup a Virtual Disk by ID.
- id [string]: a virtual disk identifier
- Returns [list of dicts]: a list of extents as dicts, containing fields:
- device : Linux device number of host disk
- start_sector : within the device
- nr_sectors : size of this extent
- type : set to \'VD Extent\'
-
- part_device : Linux device no of host partition
- part_start_sector : within the partition
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("-- types int")
- cu.execute("""SELECT COUNT(*)
- FROM vdisks
- WHERE (expiry_time > datetime('now') OR NOT expires)
- AND vdisk_id = """ + id)
- count, = cu.fetchone()
-
- if not count:
- cx.close()
- return None
-
- cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
- real_size, = cu.fetchone()
-
- # This query tells PySQLite how to convert the data returned from the
- # following query - the use of the multiplication confuses it otherwise ;-)
- # This row is significant to PySQLite but is syntactically an SQL comment.
-
- cu.execute("-- types str, int, int, int")
-
- # This SQL statement is designed so that when the results are fetched they
- # will be in the right format to return immediately.
- cu.execute("""SELECT partition, vdisk_part.part_id,
- round(part_extent_no * extent_size) as start,
- extent_size
-
- FROM vdisks NATURAL JOIN vdisk_extents
- NATURAL JOIN vdisk_part
-
- WHERE vdisk_extents.vdisk_id = """ + id
- + " ORDER BY vdisk_extents.vdisk_extent_no ASC"
- )
-
- extent_tuples = cu.fetchall()
-
- # use this function to map the results from the database into a dict
- # list of extents, for consistency with the rest of the code
- def transform ((partition, part_device, part_offset, nr_sectors)):
- return {
- # the disk device this extent is on - for passing to Xen
- 'device' : lookup_raw_partn(partition)[0]['device'],
- # the offset of this extent within the disk - for passing to Xen
- 'start_sector' : long(part_offset + lookup_raw_partn(partition)[0]['start_sector']),
- # extent size, in sectors
- 'nr_sectors' : nr_sectors,
- # partition device this extent is on (useful to know for XenoUtil fns)
- 'part_device' : part_device,
- # start sector within this partition (useful to know for XenoUtil fns)
- 'part_start_sector' : part_offset,
- # type of this extent - handy to know
- 'type' : 'VD Extent' }
-
- cx.commit()
- cx.close()
-
- extent_dicts = map(transform, extent_tuples)
-
- # calculate the over-allocation in sectors (happens because
- # we allocate whole extents)
- allocated_size = 0
- for i in extent_dicts:
- allocated_size += i['nr_sectors']
-
- over_allocation = allocated_size - real_size
-
- # trim down the last extent's length so the resulting VBD will be the
- # size requested, rather than being rounded up to the nearest extent
- extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
-
- return extent_dicts
-
-
-def vd_enlarge(vdisk_id, extra_size_mb):
- """Create a new virtual disk.
- vdisk_id [string] : ID of the virtual disk to enlarge
- extra_size_mb [int]: size in megabytes to increase the allocation by
- returns [int] : 0 on success, otherwise non-zero
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- extra_size = extra_size_mb * 2048
-
- cu.execute("-- types int")
- cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
- + " AND (expiry_time > datetime('now') OR NOT expires)")
- count, = cu.fetchone()
-
- if not count: # no such vdisk
- cx.close()
- return -1
-
- cu.execute("-- types int")
- cu.execute("""SELECT SUM(extent_size)
- FROM vdisks NATURAL JOIN vdisk_extents
- NATURAL JOIN vdisk_part
- WHERE vdisks.vdisk_id = """ + vdisk_id)
-
- real_size, = cu.fetchone() # get the true allocated size
-
- cu.execute("-- types int")
- cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
-
- old_size, = cu.fetchone()
-
-
- cu.execute("--- types int")
- cu.execute("""SELECT MAX(vdisk_extent_no)
- FROM vdisk_extents
- WHERE vdisk_id = """ + vdisk_id)
-
- counter = cu.fetchone()[0] + 1 # this stores the extent numbers
-
-
- # because of the extent-based allocation, the VD may already have more
- # allocated space than they asked for. Find out how much we really
- # need to add.
- add_size = extra_size + old_size - real_size
-
- # fetch a list of extents from the expired disks, along with information
- # about their size
- cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
- vdisk_extents.part_id, extent_size
- FROM vdisks NATURAL JOIN vdisk_extents
- NATURAL JOIN vdisk_part
- WHERE expires AND expiry_time <= datetime('now')
- ORDER BY expiry_time ASC, vdisk_extent_no DESC
- """) # aims to reuse the last extents
- # from the longest-expired disks first
-
- allocated = 0
-
- building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
- + " WHERE vdisk_id = " + vdisk_id + "; "
-
- while allocated < add_size:
- row = cu.fetchone()
- if not row:
- cx.close()
- return -1
-
- (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
- allocated += extent_size
- building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id \
- + ", " + "vdisk_extent_no = " + str(counter) \
- + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
- + " AND vdisk_id = " + str(dead_vd_id) + "; "
-
- counter += 1
-
-
- # this will execute the SQL query we build to store details of the new
- # virtual disk and allocate space to it print building_sql
- cu.execute(building_sql)
-
- cx.commit()
- cx.close()
- return 0
-
-
-def vd_undelete(vdisk_id, expiry_time):
- """Create a new virtual disk.
- vdisk_id [int]: size in megabytes for the new virtual disk
- expiry_time [int]: expiry time, in seconds from now
- returns [int]: zero on success, non-zero on failure
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- if vdisk_id == '0': # undeleting vdisk 0 isn't sane!
- return -1
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("-- types int")
- cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
- count, = cu.fetchone()
-
- if not count:
- cx.close()
- return -1
-
- cu.execute("-- types int")
- cu.execute("""SELECT SUM(extent_size)
- FROM vdisks NATURAL JOIN vdisk_extents
- NATURAL JOIN vdisk_part
- WHERE vdisks.vdisk_id = """ + vdisk_id)
-
- real_size, = cu.fetchone() # get the true allocated size
-
-
- cu.execute("-- types int")
- cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
-
- old_size, = cu.fetchone()
-
- if real_size < old_size:
- cx.close()
- return -1
-
- if expiry_time == 0:
- expires = '0'
- else:
- expires = '1'
-
- # this will execute the SQL query we build to store details of the new
- # virtual disk and allocate space to it print building_sql
- cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
- + str(expiry_time) + " seconds'), expires = " + expires
- + " WHERE vdisk_id = " + vdisk_id)
-
- cx.commit()
- cx.close()
- return 0
-
-
-
-
-def vd_list():
- """Lists all the virtual disks registered in the system.
- returns [list of dicts]
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("""SELECT vdisk_id, size, expires, expiry_time
- FROM vdisks
- WHERE (NOT expires) OR expiry_time > datetime('now')
- """)
-
- ret = cu.fetchall()
-
- cx.close()
-
- def makedicts((vdisk_id, size, expires, expiry_time)):
- return { 'vdisk_id' : str(vdisk_id), 'size': size,
- 'expires' : expires, 'expiry_time' : expiry_time }
-
- return map(makedicts, ret)
-
-
-def vd_refresh(id, expiry):
- """Change the expiry time of a virtual disk.
- id [string] : a virtual disk identifier
- expiry [int] : expiry time in seconds from now (0 = never expire)
- returns [int]: zero on success, non-zero on failure
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("-- types int")
- cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
- + " AND (expiry_time > datetime('now') OR NOT expires)")
- count, = cu.fetchone()
-
- if not count:
- cx.close()
- return -1
-
- if expiry:
- expires = 1
- expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
- else:
- expires = 0
- expiry_ts = "NULL"
-
- cu.execute("UPDATE vdisks SET expires = " + str(expires)
- + ", expiry_time = " + expiry_ts
- + " WHERE (expiry_time > datetime('now') OR NOT expires)"
- + " AND vdisk_id = " + id)
-
- cx.commit()
- cx.close()
-
- return 0
-
-
-def vd_delete(id):
- """Deletes a Virtual Disk, making its extents available for future VDs.
- id [string] : identifier for the virtual disk to delete
- returns [int] : 0 on success, -1 on failure (VD not found
- or already deleted)
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("-- types int")
- cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
- + " AND (expiry_time > datetime('now') OR NOT expires)")
- count, = cu.fetchone()
-
- if not count:
- cx.close()
- return -1
-
- cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
- + " WHERE vdisk_id = " + id)
-
- cx.commit()
- cx.close()
-
- return 0
-
-
-def vd_freespace():
- """Returns the amount of free space available for new virtual disks, in MB
- returns [int] : free space for VDs in MB
- """
-
- if not os.path.isfile(VD_DB_FILE):
- __vd_no_database()
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("-- types int")
-
- cu.execute("""SELECT SUM(extent_size)
- FROM vdisks NATURAL JOIN vdisk_extents
- NATURAL JOIN vdisk_part
- WHERE expiry_time <= datetime('now') AND expires""")
-
- sum, = cu.fetchone()
-
- cx.close()
-
- return sum / 2048
-
-
-def vd_init_db(path):
- """Initialise the VD SQLite database
- path [string]: path to the SQLite database file
- """
-
- cx = sqlite.connect(path)
- cu = cx.cursor()
-
- cu.execute(
- """CREATE TABLE vdisk_extents
- ( vdisk_extent_no INT,
- vdisk_id INT,
- part_id INT,
- part_extent_no INT )
- """)
-
- cu.execute(
- """CREATE TABLE vdisk_part
- ( part_id INT,
- partition VARCHAR,
- extent_size INT )
- """)
-
- cu.execute(
- """CREATE TABLE vdisks
- ( vdisk_id INT,
- size INT,
- expires BOOLEAN,
- expiry_time TIMESTAMP )
- """)
-
-
- cu.execute(
- """INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time )
- VALUES ( 0, 0, 1, datetime('now') )
- """)
-
- cx.commit()
- cx.close()
-
- VD_DB_FILE = path
-
-
-
-def vd_cp_to_file(vdisk_id,filename):
- """Writes the contents of a specified vdisk out into a disk file, leaving
- the original copy in the virtual disk pool."""
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- extents = vd_lookup(vdisk_id)
-
- if not extents:
- return -1
-
- file_idx = 0 # index into source file, in sectors
-
- for i in extents:
- cu.execute("""SELECT partition, extent_size FROM vdisk_part
- WHERE part_id = """ + str(i['part_device']))
-
- (partition, extent_size) = cu.fetchone()
-
- os.system("dd bs=1b if=" + partition + " of=" + filename
- + " skip=" + str(i['part_start_sector'])
- + " seek=" + str(file_idx)
- + " count=" + str(i['nr_sectors'])
- + " > /dev/null")
-
- file_idx += i['nr_sectors']
-
- cx.close()
-
- return 0 # should return -1 if something breaks
-
-
-def vd_mv_to_file(vdisk_id,filename):
- """Writes a vdisk out into a disk file and frees the space originally
- taken within the virtual disk pool.
- vdisk_id [string]: ID of the vdisk to write out
- filename [string]: file to write vdisk contents out to
- returns [int]: zero on success, nonzero on failure
- """
-
- if vd_cp_to_file(vdisk_id,filename):
- return -1
-
- if vd_delete(vdisk_id):
- return -1
-
- return 0
-
-
-def vd_read_from_file(filename,expiry):
- """Reads the contents of a file directly into a vdisk, which is
- automatically allocated to fit.
- filename [string]: file to read disk contents from
- returns [string] : vdisk ID for the destination vdisk
- """
-
- size_bytes = os.stat(filename).st_size
-
- (size_mb,leftover) = divmod(size_bytes,1048580) # size in megabytes
- if leftover > 0: size_mb += 1 # round up if not an exact number of MB
-
- vdisk_id = vd_create(size_mb, expiry)
-
- if vdisk_id < 0:
- return -1
-
- cx = sqlite.connect(VD_DB_FILE)
- cu = cx.cursor()
-
- cu.execute("""SELECT partition, extent_size, part_extent_no
- FROM vdisk_part NATURAL JOIN vdisk_extents
- WHERE vdisk_id = """ + vdisk_id + """
- ORDER BY vdisk_extent_no ASC""")
-
- extents = cu.fetchall()
-
- size_sectors = size_mb * 2048 # for feeding to dd
-
- file_idx = 0 # index into source file, in sectors
-
- def write_extent_to_vd((partition, extent_size, part_extent_no),
- file_idx, filename):
- """Write an extent out to disk and update file_idx"""
-
- os.system("dd bs=512 if=" + filename + " of=" + partition
- + " skip=" + str(file_idx)
- + " seek=" + str(part_extent_no * extent_size)
- + " count=" + str(min(extent_size, size_sectors - file_idx))
- + " > /dev/null")
-
- return extent_size
-
- for i in extents:
- file_idx += write_extent_to_vd(i, file_idx, filename)
-
- cx.close()
-
- return vdisk_id
-
-
-
-
-def vd_extents_validate(new_extents,new_writeable):
- """Validate the extents against the existing extents.
- Complains if the list supplied clashes against the extents that
- are already in use in the system.
- new_extents [list of dicts]: list of new extents, as dicts
- new_writeable [int]: 1 if they are to be writeable, 0 otherwise
- returns [int]: either the expertise level of the mapping if it doesn't
- exceed VBD_EXPERT_MODE or -1 if it does (error)
- """
-
- import Xc # this is only needed in this function
-
- xc = Xc.new()
-
- ##### Probe for explicitly created virtual disks and build a list
- ##### of extents for comparison with the ones that are being added
-
- probe = xc.vbd_probe()
-
- old_extents = [] # this will hold a list of all existing extents and
- # their writeable status, as a list of (device,
- # start, size, writeable?) tuples
-
- for vbd in probe:
- this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
- for vbd_ext in this_vbd_extents:
- vbd_ext['writeable'] = vbd['writeable']
- old_extents.append(vbd_ext)
-
- ##### Now scan /proc/mounts for compile a list of extents corresponding to
- ##### any devices mounted in DOM0. This list is added on to old_extents
-
- regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
- fd = open('/proc/mounts', "r")
-
- while True:
- line = fd.readline()
- if not line: # if we've run out of lines then stop reading
- break
-
- m = regexp.match(line)
-
- # if the regexp didn't match then it's probably a line we don't
- # care about - skip to next line
- if not m:
- continue
-
- # lookup the device
- ext_list = lookup_raw_partn(m.group(1))
-
- # if lookup failed, skip to next mounted device
- if not ext_list:
- continue
-
- # set a writeable flag as appropriate
- for ext in ext_list:
- ext['writeable'] = m.group(2) == 'rw'
-
- # now we've got here, the contents of ext_list are in a
- # suitable format to be added onto the old_extents list, ready
- # for checking against the new extents
-
- old_extents.extend(ext_list)
-
- fd.close() # close /proc/mounts
-
- ##### By this point, old_extents contains a list of extents, in
- ##### dictionary format corresponding to every extent of physical
- ##### disk that's either part of an explicitly created VBD, or is
- ##### mounted under DOM0. We now check these extents against the
- ##### proposed additions in new_extents, to see if a conflict will
- ##### happen if they are added with write status new_writeable
-
- level = 0 # this'll accumulate the max warning level
-
- # Search for clashes between the new extents and the old ones
- # Takes time O(len(new_extents) * len(old_extents))
- for new_ext in new_extents:
- for old_ext in old_extents:
- if(new_ext['device'] == old_ext['device']):
-
- new_ext_start = new_ext['start_sector']
- new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1
-
- old_ext_start = old_ext['start_sector']
- old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1
-
- if((old_ext_start <= new_ext_start <= old_ext_end) or
- (old_ext_start <= new_ext_end <= old_ext_end)):
- if (not old_ext['writeable']) and new_writeable:
- level = max(1,level)
- elif old_ext['writeable'] and (not new_writeable):
- level = max(1,level)
- elif old_ext['writeable'] and new_writeable:
- level = max(2,level)
-
-
- ##### level now holds the warning level incurred by the current
- ##### VBD setup and we complain appropriately to the user
-
-
- if level == 1:
- print >> sys.stderr, """Warning: one or more hard disk extents
- writeable by one domain are also readable by another."""
- elif level == 2:
- print >> sys.stderr, """Warning: one or more hard disk extents are
- writeable by two or more domains simultaneously."""
-
- if level > VBD_EXPERT_MODE:
- print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
- at the current safety level (%d).""" % VBD_EXPERT_MODE
- level = -1
-
- return level
-
sources = ["Xc.c"])
setup(name = "Xc", version = "1.0", ext_modules = [module])
-
-setup(name = "XenoUtil", version = "1.0", py_modules = ["XenoUtil"])
--- /dev/null
+
+all:
+ python setup.py build
+
+install: all
+ if [ "$(prefix)" = "" ]; then python setup.py install; \
+ else python setup.py install --home="$(prefix)"; fi
+
+clean:
+ rm -rf build *.pyc *.pyo *.o *.a *~
--- /dev/null
+
+##############################################
+# Console client for Xen guest OSes
+# Copyright (c) 2004, K A Fraser
+##############################################
+
+import errno, os, signal, socket, struct, sys, termios
+
+def __child_death(signum, frame):
+ global stop
+ stop = True
+
+def __recv_from_sock(sock):
+ global stop
+ stop = False
+ print "************ REMOTE CONSOLE: CTRL-] TO QUIT ********"
+ while not stop:
+ try:
+ data = sock.recv(1)
+ os.write(1, data)
+ except socket.error, error:
+ if error[0] != errno.EINTR:
+ raise
+ print
+ print "************ REMOTE CONSOLE EXITED *****************"
+ os.wait()
+
+def __send_to_sock(sock):
+ while 1:
+ data = os.read(0,1)
+ if ord(data[0]) == ord(']')-64:
+ break
+ sock.send(data)
+ sys.exit(0)
+
+def connect(host,port):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack('ii', 0, 0))
+ sock.connect((host,port))
+
+ oattrs = termios.tcgetattr(0)
+ nattrs = termios.tcgetattr(0)
+ nattrs[3] = nattrs[3] & ~(termios.ECHO | termios.ICANON)
+ nattrs[6][termios.VMIN] = 1
+ nattrs[6][termios.VTIME] = 0
+ termios.tcsetattr(0, termios.TCSAFLUSH, nattrs)
+
+ try:
+ if os.fork():
+ signal.signal(signal.SIGCHLD, __child_death)
+ __recv_from_sock(sock)
+ else:
+ __send_to_sock(sock)
+ finally:
+ termios.tcsetattr(0, termios.TCSAFLUSH, oattrs)
+
+if __name__ == '__main__':
+ main(str(sys.argv[1]),int(sys.argv[2]))
--- /dev/null
+import os, re, socket, string, sys, tempfile
+
+##### Module variables
+
+"""Location of the Virtual Disk management database.
+ defaults to /var/db/xen_vdisks.sqlite
+"""
+VD_DB_FILE = "/var/db/xen_vdisks.sqlite"
+
+"""VBD expertise level - determines the strictness of the sanity checking.
+ This mode determines the level of complaints when disk sharing occurs
+ through the current VBD mappings.
+ 0 - only allow shared mappings if both domains have r/o access (always OK)
+ 1 - also allow sharing with one dom r/w and the other r/o
+ 2 - allow sharing with both doms r/w
+"""
+VBD_EXPERT_MODE = 0
+
+##### Module initialisation
+
+try:
+ # try to import sqlite (not everyone will have it installed)
+ import sqlite
+except ImportError:
+ # on failure, just catch the error, don't do anything
+ pass
+
+
+##### Networking-related functions
+
+def get_current_ipaddr(dev='eth0'):
+ """Return a string containing the primary IP address for the given
+ network interface (default 'eth0').
+ """
+ fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
+ lines = fd.readlines()
+ for line in lines:
+ m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
+ line )
+ if m:
+ return m.group(1)
+ return None
+
+def get_current_ipmask(dev='eth0'):
+ """Return a string containing the primary IP netmask for the given
+ network interface (default 'eth0').
+ """
+ fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
+ lines = fd.readlines()
+ for line in lines:
+ m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
+ line )
+ if m:
+ return m.group(1)
+ return None
+
+def get_current_ipgw(dev='eth0'):
+ """Return a string containing the IP gateway for the given
+ network interface (default 'eth0').
+ """
+ fd = os.popen( '/sbin/route -n' )
+ lines = fd.readlines()
+ for line in lines:
+ m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
+ '\s+\S+\s+\S*G.*' + dev + '.*', line )
+ if m:
+ return m.group(1)
+ return None
+
+def setup_vfr_rules_for_vif(dom,vif,addr):
+ """Takes a tuple ( domain-id, vif-id, ip-addr ), where the ip-addr
+ is expressed as a textual dotted quad, and set up appropriate routing
+ rules in Xen. No return value.
+ """
+ fd = os.open( '/proc/xen/vfr', os.O_WRONLY )
+ if ( re.search( '169\.254', addr) ):
+ os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
+ ' srcaddrmask=255.255.255.255' +
+ ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
+ ' dstdom=0 dstidx=0 proto=any\n' )
+ else:
+ os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
+ ' srcaddrmask=255.255.255.255' +
+ ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
+ ' dst=PHYS proto=any\n' )
+ os.write( fd, 'ADD ACCEPT dstaddr=' + addr +
+ ' dstaddrmask=255.255.255.255' +
+ ' src=ANY' +
+ ' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
+ ' proto=any\n' )
+ os.close( fd )
+ return None
+
+def add_offset_to_ip( ip, off ):
+ l = string.split(ip,'.')
+ a = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
+ (string.atoi(l[2])<<8) | string.atoi(l[3]) ) + off
+
+ return '%d.%d.%d.%d' % ( ((a>>24)&0xff), ((a>>16)&0xff),
+ ((a>>8)&0xff), (a&0xff) )
+
+def check_subnet( ip, network, netmask ):
+ l = string.split(ip,'.')
+ n_ip = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
+ (string.atoi(l[2])<<8) | string.atoi(l[3]) )
+
+ l = string.split(network,'.')
+ n_net = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
+ (string.atoi(l[2])<<8) | string.atoi(l[3]) )
+
+ l = string.split(netmask,'.')
+ n_mask = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
+ (string.atoi(l[2])<<8) | string.atoi(l[3]) )
+
+ return (n_ip&n_mask)==(n_net&n_mask)
+
+
+##### VBD-related Functions
+
+def blkdev_name_to_number(name):
+ """Take the given textual block-device name (e.g., '/dev/sda1',
+ 'hda') and return the device number used by the OS. """
+
+ if not re.match( '/dev/', name ):
+ name = '/dev/' + name
+
+ return os.stat(name).st_rdev
+
+# lookup_blkdev_partn_info( '/dev/sda3' )
+def lookup_raw_partn(partition):
+ """Take the given block-device name (e.g., '/dev/sda1', 'hda')
+ and return a dictionary { device, start_sector,
+ nr_sectors, type }
+ device: Device number of the given partition
+ start_sector: Index of first sector of the partition
+ nr_sectors: Number of sectors comprising this partition
+ type: 'Disk' or identifying name for partition type
+ """
+
+ if not re.match( '/dev/', partition ):
+ partition = '/dev/' + partition
+
+ drive = re.split( '[0-9]', partition )[0]
+
+ if drive == partition:
+ fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
+ line = fd.readline()
+ if line:
+ return [ { 'device' : blkdev_name_to_number(drive),
+ 'start_sector' : long(0),
+ 'nr_sectors' : long(line) * 2,
+ 'type' : 'Disk' } ]
+ return None
+
+ # determine position on disk
+ fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
+
+ #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
+ lines = fd.readlines()
+ for line in lines:
+ m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
+ 'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
+ if m:
+ return [ { 'device' : blkdev_name_to_number(drive),
+ 'start_sector' : long(m.group(1)),
+ 'nr_sectors' : long(m.group(2)),
+ 'type' : m.group(3) } ]
+
+ return None
+
+def lookup_disk_uname( uname ):
+ """Lookup a list of segments for either a physical or a virtual device.
+ uname [string]: name of the device in the format \'vd:id\' for a virtual
+ disk, or \'phy:dev\' for a physical device
+ returns [list of dicts]: list of extents that make up the named device
+ """
+ ( type, d_name ) = string.split( uname, ':' )
+
+ if type == "phy":
+ segments = lookup_raw_partn( d_name )
+ elif type == "vd":
+ segments = vd_lookup( d_name )
+
+ return segments
+
+
+##### Management of the Xen control daemon
+##### (c) Keir Fraser, University of Cambridge
+
+def xend_control_message( message ):
+ """Takes a textual control message and sends it to the 'xend' Xen
+ control daemon. Returns a dictionary containing the daemon's multi-part
+ response."""
+ tmpdir = tempfile.mkdtemp()
+ try:
+ ctl = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+ ctl.bind(tmpdir+'/sock')
+ ctl.sendto(message, '/var/run/xend/management_sock')
+ data, addr = ctl.recvfrom(2048)
+ ctl.close()
+ finally:
+ if os.path.exists(tmpdir+'/sock'):
+ os.unlink(tmpdir+'/sock')
+ if os.path.exists(tmpdir):
+ os.rmdir(tmpdir)
+ return eval(data)
+
+
+##### VD Management-related functions
+
+##### By Mark Williamson, <mark.a.williamson@intel.com>
+##### (C) Intel Research Cambridge
+
+# TODO:
+#
+# Plenty of room for enhancement to this functionality (contributions
+# welcome - and then you get to have your name in the source ;-)...
+#
+# vd_unformat() : want facilities to unallocate virtual disk
+# partitions, possibly migrating virtual disks of them, with checks to see if
+# it's safe and options to force it anyway
+#
+# vd_create() : should have an optional argument specifying a physical
+# disk preference - useful to allocate for guest doms to do RAID
+#
+# vd_undelete() : add ability to "best effort" undelete as much of a
+# vdisk as is left in the case that some of it has already been
+# reallocated. Some people might still be able to recover some of
+# their data this way, even if some of the disk has disappeared.
+#
+# It'd be nice if we could wipe virtual disks for security purposes -
+# should be easy to do this using dev if=/dev/{zero,random} on each
+# extent in turn. There could be another optional flag to vd_create
+# in order to allow this.
+#
+# Error codes could be more expressive - i.e. actually tell why the
+# error occurred rather than "it broke". Currently the code avoids
+# using exceptions to make control scripting simpler and more
+# accessible to beginners - therefore probably should just use more
+# return codes.
+#
+# Enhancements / additions to the example scripts are also welcome:
+# some people will interact with this code mostly through those
+# scripts.
+#
+# More documentation of how this stuff should be used is always nice -
+# if you have a novel configuration that you feel isn't discussed
+# enough in the HOWTO (which is currently a work in progress), feel
+# free to contribute a walkthrough, or something more substantial.
+#
+
+
+def __vd_no_database():
+ """Called when no database found - exits with an error
+ """
+ print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE
+ sys.exit(1)
+
+
+def vd_format(partition, extent_size_mb):
+ """Format a partition or drive for use a virtual disk storage.
+ partition [string]: device file representing the partition
+ extent_size_mb [string]: extent size in megabytes to use on this disk
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ vd_init_db(VD_DB_FILE)
+
+ if not re.match( '/dev/', partition ):
+ partition = '/dev/' + partition
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("select * from vdisk_part where partition = \'"
+ + partition + "\'")
+ row = cu.fetchone()
+
+ extent_size = extent_size_mb * 2048 # convert megabytes to sectors
+
+ if not row:
+ part_info = lookup_raw_partn(partition)[0]
+
+ cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " +
+ "VALUES ( \'" + partition + "\', "
+ + str(blkdev_name_to_number(partition))
+ + ", " + str(extent_size) + ")")
+
+
+ cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents "
+ + "WHERE vdisk_id = 0")
+
+ max_id, = cu.fetchone()
+
+ if max_id != None:
+ new_id = max_id + 1
+ else:
+ new_id = 0
+
+ num_extents = part_info['nr_sectors'] / extent_size
+
+ for i in range(num_extents):
+ sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id,
+ part_id, part_extent_no)
+ VALUES ("""+ str(new_id + i) + ", 0, "\
+ + str(blkdev_name_to_number(partition))\
+ + ", " + str(num_extents - (i + 1)) + ")"
+ cu.execute(sql)
+
+ cx.commit()
+ cx.close()
+ return 0
+
+
+def vd_create(size_mb, expiry):
+ """Create a new virtual disk.
+ size_mb [int]: size in megabytes for the new virtual disk
+ expiry [int]: expiry time in seconds from now
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ size = size_mb * 2048
+
+ cu.execute("SELECT max(vdisk_id) FROM vdisks")
+ max_id, = cu.fetchone()
+ new_id = int(max_id) + 1
+
+ # fetch a list of extents from the expired disks, along with information
+ # about their size
+ cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
+ vdisk_extents.part_id, extent_size
+ FROM vdisks NATURAL JOIN vdisk_extents
+ NATURAL JOIN vdisk_part
+ WHERE expires AND expiry_time <= datetime('now')
+ ORDER BY expiry_time ASC, vdisk_extent_no DESC
+ """) # aims to reuse the last extents
+ # from the longest-expired disks first
+
+ allocated = 0
+
+ if expiry:
+ expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
+ expires = 1
+ else:
+ expiry_ts = "NULL"
+ expires = 0
+
+ # we'll use this to build the SQL statement we want
+ building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
+ +" VALUES ("+str(new_id)+", "+str(size)+ ", " \
+ + str(expires) + ", " + expiry_ts + "); "
+
+ counter = 0
+
+ while allocated < size:
+ row = cu.fetchone()
+ if not row:
+ print "ran out of space, having allocated %d meg of %d" % (allocated, size)
+ cx.close()
+ return -1
+
+
+ (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
+ allocated += extent_size
+ building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \
+ + ", " + "vdisk_extent_no = " + str(counter) \
+ + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
+ + " AND vdisk_id = " + str(vdisk_id) + "; "
+
+ counter += 1
+
+
+ # this will execute the SQL query we build to store details of the new
+ # virtual disk and allocate space to it print building_sql
+ cu.execute(building_sql)
+
+ cx.commit()
+ cx.close()
+ return str(new_id)
+
+
+def vd_lookup(id):
+ """Lookup a Virtual Disk by ID.
+ id [string]: a virtual disk identifier
+ Returns [list of dicts]: a list of extents as dicts, containing fields:
+ device : Linux device number of host disk
+ start_sector : within the device
+ nr_sectors : size of this extent
+ type : set to \'VD Extent\'
+
+ part_device : Linux device no of host partition
+ part_start_sector : within the partition
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("-- types int")
+ cu.execute("""SELECT COUNT(*)
+ FROM vdisks
+ WHERE (expiry_time > datetime('now') OR NOT expires)
+ AND vdisk_id = """ + id)
+ count, = cu.fetchone()
+
+ if not count:
+ cx.close()
+ return None
+
+ cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
+ real_size, = cu.fetchone()
+
+ # This query tells PySQLite how to convert the data returned from the
+ # following query - the use of the multiplication confuses it otherwise ;-)
+ # This row is significant to PySQLite but is syntactically an SQL comment.
+
+ cu.execute("-- types str, int, int, int")
+
+ # This SQL statement is designed so that when the results are fetched they
+ # will be in the right format to return immediately.
+ cu.execute("""SELECT partition, vdisk_part.part_id,
+ round(part_extent_no * extent_size) as start,
+ extent_size
+
+ FROM vdisks NATURAL JOIN vdisk_extents
+ NATURAL JOIN vdisk_part
+
+ WHERE vdisk_extents.vdisk_id = """ + id
+ + " ORDER BY vdisk_extents.vdisk_extent_no ASC"
+ )
+
+ extent_tuples = cu.fetchall()
+
+ # use this function to map the results from the database into a dict
+ # list of extents, for consistency with the rest of the code
+ def transform ((partition, part_device, part_offset, nr_sectors)):
+ return {
+ # the disk device this extent is on - for passing to Xen
+ 'device' : lookup_raw_partn(partition)[0]['device'],
+ # the offset of this extent within the disk - for passing to Xen
+ 'start_sector' : long(part_offset + lookup_raw_partn(partition)[0]['start_sector']),
+ # extent size, in sectors
+ 'nr_sectors' : nr_sectors,
+ # partition device this extent is on (useful to know for xenctl.utils fns)
+ 'part_device' : part_device,
+ # start sector within this partition (useful to know for xenctl.utils fns)
+ 'part_start_sector' : part_offset,
+ # type of this extent - handy to know
+ 'type' : 'VD Extent' }
+
+ cx.commit()
+ cx.close()
+
+ extent_dicts = map(transform, extent_tuples)
+
+ # calculate the over-allocation in sectors (happens because
+ # we allocate whole extents)
+ allocated_size = 0
+ for i in extent_dicts:
+ allocated_size += i['nr_sectors']
+
+ over_allocation = allocated_size - real_size
+
+ # trim down the last extent's length so the resulting VBD will be the
+ # size requested, rather than being rounded up to the nearest extent
+ extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
+
+ return extent_dicts
+
+
+def vd_enlarge(vdisk_id, extra_size_mb):
+ """Create a new virtual disk.
+ vdisk_id [string] : ID of the virtual disk to enlarge
+ extra_size_mb [int]: size in megabytes to increase the allocation by
+ returns [int] : 0 on success, otherwise non-zero
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ extra_size = extra_size_mb * 2048
+
+ cu.execute("-- types int")
+ cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
+ + " AND (expiry_time > datetime('now') OR NOT expires)")
+ count, = cu.fetchone()
+
+ if not count: # no such vdisk
+ cx.close()
+ return -1
+
+ cu.execute("-- types int")
+ cu.execute("""SELECT SUM(extent_size)
+ FROM vdisks NATURAL JOIN vdisk_extents
+ NATURAL JOIN vdisk_part
+ WHERE vdisks.vdisk_id = """ + vdisk_id)
+
+ real_size, = cu.fetchone() # get the true allocated size
+
+ cu.execute("-- types int")
+ cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
+
+ old_size, = cu.fetchone()
+
+
+ cu.execute("--- types int")
+ cu.execute("""SELECT MAX(vdisk_extent_no)
+ FROM vdisk_extents
+ WHERE vdisk_id = """ + vdisk_id)
+
+ counter = cu.fetchone()[0] + 1 # this stores the extent numbers
+
+
+ # because of the extent-based allocation, the VD may already have more
+ # allocated space than they asked for. Find out how much we really
+ # need to add.
+ add_size = extra_size + old_size - real_size
+
+ # fetch a list of extents from the expired disks, along with information
+ # about their size
+ cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
+ vdisk_extents.part_id, extent_size
+ FROM vdisks NATURAL JOIN vdisk_extents
+ NATURAL JOIN vdisk_part
+ WHERE expires AND expiry_time <= datetime('now')
+ ORDER BY expiry_time ASC, vdisk_extent_no DESC
+ """) # aims to reuse the last extents
+ # from the longest-expired disks first
+
+ allocated = 0
+
+ building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
+ + " WHERE vdisk_id = " + vdisk_id + "; "
+
+ while allocated < add_size:
+ row = cu.fetchone()
+ if not row:
+ cx.close()
+ return -1
+
+ (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
+ allocated += extent_size
+ building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id \
+ + ", " + "vdisk_extent_no = " + str(counter) \
+ + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
+ + " AND vdisk_id = " + str(dead_vd_id) + "; "
+
+ counter += 1
+
+
+ # this will execute the SQL query we build to store details of the new
+ # virtual disk and allocate space to it print building_sql
+ cu.execute(building_sql)
+
+ cx.commit()
+ cx.close()
+ return 0
+
+
+def vd_undelete(vdisk_id, expiry_time):
+ """Create a new virtual disk.
+ vdisk_id [int]: size in megabytes for the new virtual disk
+ expiry_time [int]: expiry time, in seconds from now
+ returns [int]: zero on success, non-zero on failure
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ if vdisk_id == '0': # undeleting vdisk 0 isn't sane!
+ return -1
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("-- types int")
+ cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
+ count, = cu.fetchone()
+
+ if not count:
+ cx.close()
+ return -1
+
+ cu.execute("-- types int")
+ cu.execute("""SELECT SUM(extent_size)
+ FROM vdisks NATURAL JOIN vdisk_extents
+ NATURAL JOIN vdisk_part
+ WHERE vdisks.vdisk_id = """ + vdisk_id)
+
+ real_size, = cu.fetchone() # get the true allocated size
+
+
+ cu.execute("-- types int")
+ cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
+
+ old_size, = cu.fetchone()
+
+ if real_size < old_size:
+ cx.close()
+ return -1
+
+ if expiry_time == 0:
+ expires = '0'
+ else:
+ expires = '1'
+
+ # this will execute the SQL query we build to store details of the new
+ # virtual disk and allocate space to it print building_sql
+ cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
+ + str(expiry_time) + " seconds'), expires = " + expires
+ + " WHERE vdisk_id = " + vdisk_id)
+
+ cx.commit()
+ cx.close()
+ return 0
+
+
+
+
+def vd_list():
+ """Lists all the virtual disks registered in the system.
+ returns [list of dicts]
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("""SELECT vdisk_id, size, expires, expiry_time
+ FROM vdisks
+ WHERE (NOT expires) OR expiry_time > datetime('now')
+ """)
+
+ ret = cu.fetchall()
+
+ cx.close()
+
+ def makedicts((vdisk_id, size, expires, expiry_time)):
+ return { 'vdisk_id' : str(vdisk_id), 'size': size,
+ 'expires' : expires, 'expiry_time' : expiry_time }
+
+ return map(makedicts, ret)
+
+
+def vd_refresh(id, expiry):
+ """Change the expiry time of a virtual disk.
+ id [string] : a virtual disk identifier
+ expiry [int] : expiry time in seconds from now (0 = never expire)
+ returns [int]: zero on success, non-zero on failure
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("-- types int")
+ cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
+ + " AND (expiry_time > datetime('now') OR NOT expires)")
+ count, = cu.fetchone()
+
+ if not count:
+ cx.close()
+ return -1
+
+ if expiry:
+ expires = 1
+ expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
+ else:
+ expires = 0
+ expiry_ts = "NULL"
+
+ cu.execute("UPDATE vdisks SET expires = " + str(expires)
+ + ", expiry_time = " + expiry_ts
+ + " WHERE (expiry_time > datetime('now') OR NOT expires)"
+ + " AND vdisk_id = " + id)
+
+ cx.commit()
+ cx.close()
+
+ return 0
+
+
+def vd_delete(id):
+ """Deletes a Virtual Disk, making its extents available for future VDs.
+ id [string] : identifier for the virtual disk to delete
+ returns [int] : 0 on success, -1 on failure (VD not found
+ or already deleted)
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("-- types int")
+ cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
+ + " AND (expiry_time > datetime('now') OR NOT expires)")
+ count, = cu.fetchone()
+
+ if not count:
+ cx.close()
+ return -1
+
+ cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
+ + " WHERE vdisk_id = " + id)
+
+ cx.commit()
+ cx.close()
+
+ return 0
+
+
+def vd_freespace():
+ """Returns the amount of free space available for new virtual disks, in MB
+ returns [int] : free space for VDs in MB
+ """
+
+ if not os.path.isfile(VD_DB_FILE):
+ __vd_no_database()
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("-- types int")
+
+ cu.execute("""SELECT SUM(extent_size)
+ FROM vdisks NATURAL JOIN vdisk_extents
+ NATURAL JOIN vdisk_part
+ WHERE expiry_time <= datetime('now') AND expires""")
+
+ sum, = cu.fetchone()
+
+ cx.close()
+
+ return sum / 2048
+
+
+def vd_init_db(path):
+ """Initialise the VD SQLite database
+ path [string]: path to the SQLite database file
+ """
+
+ cx = sqlite.connect(path)
+ cu = cx.cursor()
+
+ cu.execute(
+ """CREATE TABLE vdisk_extents
+ ( vdisk_extent_no INT,
+ vdisk_id INT,
+ part_id INT,
+ part_extent_no INT )
+ """)
+
+ cu.execute(
+ """CREATE TABLE vdisk_part
+ ( part_id INT,
+ partition VARCHAR,
+ extent_size INT )
+ """)
+
+ cu.execute(
+ """CREATE TABLE vdisks
+ ( vdisk_id INT,
+ size INT,
+ expires BOOLEAN,
+ expiry_time TIMESTAMP )
+ """)
+
+
+ cu.execute(
+ """INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time )
+ VALUES ( 0, 0, 1, datetime('now') )
+ """)
+
+ cx.commit()
+ cx.close()
+
+ VD_DB_FILE = path
+
+
+
+def vd_cp_to_file(vdisk_id,filename):
+ """Writes the contents of a specified vdisk out into a disk file, leaving
+ the original copy in the virtual disk pool."""
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ extents = vd_lookup(vdisk_id)
+
+ if not extents:
+ return -1
+
+ file_idx = 0 # index into source file, in sectors
+
+ for i in extents:
+ cu.execute("""SELECT partition, extent_size FROM vdisk_part
+ WHERE part_id = """ + str(i['part_device']))
+
+ (partition, extent_size) = cu.fetchone()
+
+ os.system("dd bs=1b if=" + partition + " of=" + filename
+ + " skip=" + str(i['part_start_sector'])
+ + " seek=" + str(file_idx)
+ + " count=" + str(i['nr_sectors'])
+ + " > /dev/null")
+
+ file_idx += i['nr_sectors']
+
+ cx.close()
+
+ return 0 # should return -1 if something breaks
+
+
+def vd_mv_to_file(vdisk_id,filename):
+ """Writes a vdisk out into a disk file and frees the space originally
+ taken within the virtual disk pool.
+ vdisk_id [string]: ID of the vdisk to write out
+ filename [string]: file to write vdisk contents out to
+ returns [int]: zero on success, nonzero on failure
+ """
+
+ if vd_cp_to_file(vdisk_id,filename):
+ return -1
+
+ if vd_delete(vdisk_id):
+ return -1
+
+ return 0
+
+
+def vd_read_from_file(filename,expiry):
+ """Reads the contents of a file directly into a vdisk, which is
+ automatically allocated to fit.
+ filename [string]: file to read disk contents from
+ returns [string] : vdisk ID for the destination vdisk
+ """
+
+ size_bytes = os.stat(filename).st_size
+
+ (size_mb,leftover) = divmod(size_bytes,1048580) # size in megabytes
+ if leftover > 0: size_mb += 1 # round up if not an exact number of MB
+
+ vdisk_id = vd_create(size_mb, expiry)
+
+ if vdisk_id < 0:
+ return -1
+
+ cx = sqlite.connect(VD_DB_FILE)
+ cu = cx.cursor()
+
+ cu.execute("""SELECT partition, extent_size, part_extent_no
+ FROM vdisk_part NATURAL JOIN vdisk_extents
+ WHERE vdisk_id = """ + vdisk_id + """
+ ORDER BY vdisk_extent_no ASC""")
+
+ extents = cu.fetchall()
+
+ size_sectors = size_mb * 2048 # for feeding to dd
+
+ file_idx = 0 # index into source file, in sectors
+
+ def write_extent_to_vd((partition, extent_size, part_extent_no),
+ file_idx, filename):
+ """Write an extent out to disk and update file_idx"""
+
+ os.system("dd bs=512 if=" + filename + " of=" + partition
+ + " skip=" + str(file_idx)
+ + " seek=" + str(part_extent_no * extent_size)
+ + " count=" + str(min(extent_size, size_sectors - file_idx))
+ + " > /dev/null")
+
+ return extent_size
+
+ for i in extents:
+ file_idx += write_extent_to_vd(i, file_idx, filename)
+
+ cx.close()
+
+ return vdisk_id
+
+
+
+
+def vd_extents_validate(new_extents,new_writeable):
+ """Validate the extents against the existing extents.
+ Complains if the list supplied clashes against the extents that
+ are already in use in the system.
+ new_extents [list of dicts]: list of new extents, as dicts
+ new_writeable [int]: 1 if they are to be writeable, 0 otherwise
+ returns [int]: either the expertise level of the mapping if it doesn't
+ exceed VBD_EXPERT_MODE or -1 if it does (error)
+ """
+
+ import Xc # this is only needed in this function
+
+ xc = Xc.new()
+
+ ##### Probe for explicitly created virtual disks and build a list
+ ##### of extents for comparison with the ones that are being added
+
+ probe = xc.vbd_probe()
+
+ old_extents = [] # this will hold a list of all existing extents and
+ # their writeable status, as a list of (device,
+ # start, size, writeable?) tuples
+
+ for vbd in probe:
+ this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
+ for vbd_ext in this_vbd_extents:
+ vbd_ext['writeable'] = vbd['writeable']
+ old_extents.append(vbd_ext)
+
+ ##### Now scan /proc/mounts for compile a list of extents corresponding to
+ ##### any devices mounted in DOM0. This list is added on to old_extents
+
+ regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
+ fd = open('/proc/mounts', "r")
+
+ while True:
+ line = fd.readline()
+ if not line: # if we've run out of lines then stop reading
+ break
+
+ m = regexp.match(line)
+
+ # if the regexp didn't match then it's probably a line we don't
+ # care about - skip to next line
+ if not m:
+ continue
+
+ # lookup the device
+ ext_list = lookup_raw_partn(m.group(1))
+
+ # if lookup failed, skip to next mounted device
+ if not ext_list:
+ continue
+
+ # set a writeable flag as appropriate
+ for ext in ext_list:
+ ext['writeable'] = m.group(2) == 'rw'
+
+ # now we've got here, the contents of ext_list are in a
+ # suitable format to be added onto the old_extents list, ready
+ # for checking against the new extents
+
+ old_extents.extend(ext_list)
+
+ fd.close() # close /proc/mounts
+
+ ##### By this point, old_extents contains a list of extents, in
+ ##### dictionary format corresponding to every extent of physical
+ ##### disk that's either part of an explicitly created VBD, or is
+ ##### mounted under DOM0. We now check these extents against the
+ ##### proposed additions in new_extents, to see if a conflict will
+ ##### happen if they are added with write status new_writeable
+
+ level = 0 # this'll accumulate the max warning level
+
+ # Search for clashes between the new extents and the old ones
+ # Takes time O(len(new_extents) * len(old_extents))
+ for new_ext in new_extents:
+ for old_ext in old_extents:
+ if(new_ext['device'] == old_ext['device']):
+
+ new_ext_start = new_ext['start_sector']
+ new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1
+
+ old_ext_start = old_ext['start_sector']
+ old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1
+
+ if((old_ext_start <= new_ext_start <= old_ext_end) or
+ (old_ext_start <= new_ext_end <= old_ext_end)):
+ if (not old_ext['writeable']) and new_writeable:
+ level = max(1,level)
+ elif old_ext['writeable'] and (not new_writeable):
+ level = max(1,level)
+ elif old_ext['writeable'] and new_writeable:
+ level = max(2,level)
+
+
+ ##### level now holds the warning level incurred by the current
+ ##### VBD setup and we complain appropriately to the user
+
+
+ if level == 1:
+ print >> sys.stderr, """Warning: one or more hard disk extents
+ writeable by one domain are also readable by another."""
+ elif level == 2:
+ print >> sys.stderr, """Warning: one or more hard disk extents are
+ writeable by two or more domains simultaneously."""
+
+ if level > VBD_EXPERT_MODE:
+ print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
+ at the current safety level (%d).""" % VBD_EXPERT_MODE
+ level = -1
+
+ return level
+
--- /dev/null
+
+from distutils.core import setup, Extension
+
+setup(name = "xenctl",
+ version = "1.0",
+ packages = ["xenctl"],
+ package_dir = { "xenctl" : "lib" },
+ )